package com.zxjbyte.yiyi.extension.monitor.service.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.unit.DataSizeUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.system.*;
import cn.hutool.system.oshi.CpuInfo;
import cn.hutool.system.oshi.CpuTicks;
import cn.hutool.system.oshi.OshiUtil;
import com.google.common.collect.Lists;
import com.zxjbyte.yiyi.extension.monitor.domain.vo.NetworkInfoVO;
import com.zxjbyte.yiyi.extension.monitor.domain.vo.RedisInfoVO;
import com.zxjbyte.yiyi.extension.monitor.domain.vo.ServerInfoVO;
import com.zxjbyte.yiyi.extension.monitor.util.NetWorkUtil;
import com.zxjbyte.yiyi.framework.redis.manager.RedisDao;
import com.zxjbyte.yiyi.extension.monitor.service.MonitorService;
import org.springframework.data.redis.connection.RedisServerCommands;
import org.springframework.stereotype.Service;
import oshi.hardware.CentralProcessor;
import oshi.hardware.GlobalMemory;
import oshi.software.os.FileSystem;
import oshi.software.os.OperatingSystem;

import javax.annotation.Resource;
import java.lang.management.RuntimeMXBean;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicLong;

/**
 * @Author zhangxingjia
 * @Date 2024/6/20 16:36
 * @Version: 1.0
 */
@Service
public class MonitorServiceImpl implements MonitorService {

    @Resource
    private RedisDao redisDao;

    @Override
    public ServerInfoVO serverInfo() {
        ServerInfoVO vo = new ServerInfoVO();
        // cpu信息
        {
            CentralProcessor processor = OshiUtil.getProcessor();
            CpuInfo cpu = OshiUtil.getCpuInfo();
            ServerInfoVO.CpuInfo cpuInfo = new ServerInfoVO.CpuInfo();
            cpuInfo.setCpuName(processor.getProcessorIdentifier().getName());
            cpuInfo.setCpuNum(processor.getPhysicalPackageCount());
            cpuInfo.setCpuPhysicalCoreNum(processor.getPhysicalProcessorCount());
            cpuInfo.setCpuLogicalCoreNum(cpu.getCpuNum());
            cpuInfo.setCpuSysUseRate(cpu.getSys() + "%");
            cpuInfo.setCpuUserUseRate(cpu.getUser() + "%");
            cpuInfo.setCpuWaitRate(cpu.getWait() + "%");
            cpuInfo.setCpuFreeRate(cpu.getFree() + "%");
            CpuTicks ticks = cpu.getTicks();
            // 空闲时间片
            long totalIdle = ticks.getIdle() + ticks.getIoWait();
            // 总的活动时间片
            long totalCpu = ticks.totalCpu();
            cpuInfo.setCpuTotalUseRate(NumberUtil.mul(NumberUtil.div(NumberUtil.sub(totalCpu, totalIdle), totalCpu, 4), 100) + "%");
            vo.setCpuInfo(cpuInfo);
        }
        // 内存信息
        {
            GlobalMemory memory = OshiUtil.getMemory();
            ServerInfoVO.MemoryInfo memoryInfo = new ServerInfoVO.MemoryInfo();
            long used = memory.getTotal() - memory.getAvailable();
            memoryInfo.setMemoryTotal(DataSizeUtil.format(memory.getTotal()));
            memoryInfo.setMemoryUsed(DataSizeUtil.format(used));
            memoryInfo.setMemoryFree(DataSizeUtil.format(memory.getAvailable()));
            memoryInfo.setMemoryUseRate(NumberUtil.mul(NumberUtil.div(used, memory.getTotal(), 4), 100) + "%");
            vo.setMemoryInfo(memoryInfo);
        }
        // 存储信息
        {
            ServerInfoVO.StorageInfo storageInfo = new ServerInfoVO.StorageInfo();
            OperatingSystem operatingSystem = OshiUtil.getOs();
            FileSystem fileSystem = operatingSystem.getFileSystem();
            AtomicLong storageTotal = new AtomicLong();
            AtomicLong storageUsed = new AtomicLong();
            AtomicLong storageFree = new AtomicLong();
            fileSystem.getFileStores().forEach(osFileStore -> {
                long totalSpace = osFileStore.getTotalSpace();
                long usableSpace = osFileStore.getUsableSpace();
                long freeSpace = osFileStore.getFreeSpace();
                long usedSpace = totalSpace - usableSpace;
                storageTotal.addAndGet(totalSpace);
                storageUsed.addAndGet(usedSpace);
                storageFree.addAndGet(freeSpace);
            });
            storageInfo.setStorageTotal(DataSizeUtil.format(storageTotal.get()));
            storageInfo.setStorageUsed(DataSizeUtil.format(storageUsed.get()));
            storageInfo.setStorageFree(DataSizeUtil.format(storageFree.get()));
            storageInfo.setStorageUseRate(NumberUtil.mul(NumberUtil.div(storageUsed.doubleValue(), storageTotal.doubleValue(), 4), 100) + "%");
            vo.setStorageInfo(storageInfo);
        }
        // 服务器信息
        {
            ServerInfoVO.ServerInfo serverInfo = new ServerInfoVO.ServerInfo();
            HostInfo hostInfo = SystemUtil.getHostInfo();
            serverInfo.setServerName(hostInfo.getName());
            serverInfo.setServerIp(hostInfo.getAddress());
            OsInfo osInfo = SystemUtil.getOsInfo();
            serverInfo.setServerOs(osInfo.getName());
            serverInfo.setServerVersion(osInfo.getVersion());
            serverInfo.setServerArch(osInfo.getArch());
            vo.setServerInfo(serverInfo);
        }
        // JVM信息
        {
            ServerInfoVO.JvmInfo monitorJvmInfo = new ServerInfoVO.JvmInfo();
            RuntimeInfo runtimeInfo = SystemUtil.getRuntimeInfo();
            long maxMemory = runtimeInfo.getMaxMemory();
            long totalMemory  = runtimeInfo.getTotalMemory();
            long freeMemory = runtimeInfo.getFreeMemory();
            monitorJvmInfo.setJvmMemoryMax(DataSizeUtil.format(maxMemory));
            monitorJvmInfo.setJvmMemoryTotal(DataSizeUtil.format(totalMemory));
            monitorJvmInfo.setJvmMemoryFree(DataSizeUtil.format(freeMemory));
            monitorJvmInfo.setJvmMemoryUsable(DataSizeUtil.format(runtimeInfo.getUsableMemory()));
            // 使用已使用的内存量除以最大内存量，得到内存使用率的百分比
            double jvmUseRate = NumberUtil.mul(NumberUtil.div(NumberUtil.sub(totalMemory, freeMemory), maxMemory, 4), 100);
            monitorJvmInfo.setJvmUseRate(jvmUseRate + "%");
            JvmInfo jvmInfo = SystemUtil.getJvmInfo();
            monitorJvmInfo.setJvmName(jvmInfo.getName());
            monitorJvmInfo.setJvmVersion(jvmInfo.getVersion());
            RuntimeMXBean runtimeMXBean = SystemUtil.getRuntimeMXBean();
            monitorJvmInfo.setJvmStartTime(DateUtil.formatDateTime(DateUtil.date(runtimeMXBean.getStartTime())));
            monitorJvmInfo.setJvmRunTime(DateUtil.formatBetween(runtimeMXBean.getUptime()));
            monitorJvmInfo.setJavaVersion(SystemUtil.getJavaInfo().getVersion());
            monitorJvmInfo.setJavaPath(SystemUtil.getJavaRuntimeInfo().getHomeDir());
            vo.setJvmInfo(monitorJvmInfo);
        }
        return vo;
    }

    @Override
    public RedisInfoVO redisInfo() {
        RedisInfoVO vo = new RedisInfoVO();
        // redis缓存完整信息
        Properties info = redisDao.execute(RedisServerCommands::info);
        // redis缓存中可用键Key的总数
        Long dbSize = redisDao.execute(RedisServerCommands::dbSize);
        // redis缓存命令统计信息
        Properties commandStats = redisDao.execute(connection -> connection.info("commandstats"));
        if(Objects.nonNull(commandStats)){
            List<RedisInfoVO.CommandStat> list = Lists.newArrayList();
            commandStats.forEach((k, v) ->{
                RedisInfoVO.CommandStat commandStat = new RedisInfoVO.CommandStat();
                commandStat.setCommand(StrUtil.subAfter((String) k, "cmdstat_", false));
                commandStat.setCalls(Convert.toLong(StrUtil.subBetween((String) v, "calls=", ",")));
                commandStat.setUsec(Convert.toLong(StrUtil.subBetween((String) v, "usec=", ",")));
                list.add(commandStat);
            });
            vo.setCommandStats(list);
        }
        vo.setInfo(info);
        vo.setDbSize(dbSize);
        return vo;
    }

    @Override
    public NetworkInfoVO refreshNetwork() {
        Map<String, String> map = NetWorkUtil.getNetworkUpDownRate();
        NetworkInfoVO vo = new NetworkInfoVO();
        vo.setDownLinkRate(map.get("DOWN"));
        vo.setUpLinkRate(map.get("UP"));
        return vo;
    }


}
